//@author A0097091Y
//This class contains functionalities to support the undo/redo function

package crossoff.db;

import java.io.IOException;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import crossoff.model.GUIobject;
import crossoff.model.HistoryObject;
import crossoff.model.Task;


public class History {

	private static final Logger logger = Logger.getLogger(History.class.getName());

	private static final String MESSAGE_UNDO_ADD = "TASK RE-DELETED";
	private static final String MESSAGE_UNDO_DELETE = "TASK REINSERTED";
	private static final String MESSAGE_UNDO_EDIT = "EDITING WAS REVERTED";

	private static final String MESSAGE_UNDO_EMPTY = "NOTHING TO UNDO";

	private static final String MESSAGE_REDO_EMPTY = "NOTHING TO REDO";

	private static History history;
	private Stack<HistoryObject> undoStack;
	private Stack<HistoryObject> redoStack;

	private History() {
		undoStack = new Stack<HistoryObject>();
		redoStack = new Stack<HistoryObject>();
	}

	public static History getInstance() {
		if (history == null) {
			history = new History();
		}

		return history;
	}
	
	/**
	 * Add action to undo stack
	 * @param task
	 * @param type
	 */
	public void addToUndo(Task task, HistoryType type) {
		undoStack.push(new HistoryObject(task, type));
		logger.log(Level.INFO, "New object added to undo stack");
	}
	
	/**
	 * Add action to undo stack
	 * @param task
	 * @param type
	 * @param oldTask
	 */
	public void addToUndo(Task task, HistoryType type, Task oldTask) {
		undoStack.push(new HistoryObject(task, type, oldTask));
		logger.log(Level.INFO, "New object added to undo stack");
	}
	
	/**
	 * Remove last action from undo stack
	 */
	public void removeLastFromUndo() {
		undoStack.pop();
	}

	/**
	 * Execute undo of last action
	 * @return GUIobject containing updated ArrayList of Task objects
	 * @throws IOException
	 */
	public GUIobject executeUndo() throws IOException {
		
		if (undoStack.empty()) {
			return new GUIobject(MESSAGE_UNDO_EMPTY, "");
		}

		HistoryObject toUndo = undoStack.pop();

		GUIobject toReturn = executeReversal(toUndo);

		redoStack.push(toUndo);

		logger.log(Level.INFO, "Undo action done" + toUndo.getType().toString());
		return toReturn;
	}
	
	/**
	 * Execute redo of last undone action
	 * @return GUIobject containing updated ArrayList of Task objects
	 * @throws IOException
	 */
	public GUIobject executeRedo() throws IOException {
		if (redoStack.empty()) {
			return new GUIobject(MESSAGE_REDO_EMPTY, "");
		}

		HistoryObject toRedo = redoStack.pop();
		GUIobject toReturn = executeReversal(toRedo);

		undoStack.push(toRedo);

		logger.log(Level.INFO, "Redo action done" + toRedo.getType().toString());
		return toReturn;
	}
	
	//Carry out the reversal of a given action
	private GUIobject executeReversal(HistoryObject toReverse)
			throws IOException {
		int taskIDToReverse = toReverse.getTask().getTaskID();
		Task temp;

		if (toReverse.getType() == HistoryType.ADDED) {
			Storage.getInstance().deleteTask(taskIDToReverse);
			toReverse.setType(HistoryType.DELETED);
			return new GUIobject(MESSAGE_UNDO_ADD, "");
		} else if (toReverse.getType() == HistoryType.DELETED) {
			Storage.getInstance().addTaskToFile(toReverse.getTask());
			toReverse.setType(HistoryType.ADDED);
			return new GUIobject(MESSAGE_UNDO_DELETE, "");
		}

		else {

			Storage.getInstance().deleteTask(taskIDToReverse);
			Storage.getInstance().addTaskToFile(toReverse.getOldTask());

			temp = toReverse.getTask();
			toReverse.setTask(toReverse.getOldTask());
			toReverse.setOldTask(temp);

			return new GUIobject(MESSAGE_UNDO_EDIT, "");
		}
	}

	/**
	 * Used to clear the redo stack
	 */
	public void clearRedoStack() {
		redoStack = new Stack<HistoryObject>();
	}
}
